/*
 * HkMediaChannel.cpp
 *
 *  Created on: 2017年8月20日
 *      Author: terry
 */

#include "HkMediaChannel.h"
#include "CLog.h"
#include "TFileUtil.h"
#include "AppConfig.h"


namespace av
{
	 

HkMediaChannel::HkMediaChannel():
	m_loginId(),
	m_speakHandle(-1),
	m_audioPts(),
	m_dumpEnabled(),
	m_audioType()
{
}

HkMediaChannel::~HkMediaChannel()
{
	close();
}

void HkMediaChannel::setDevice(long loginId, const std::string& id, const std::string& name)
{
	m_loginId = loginId;
	m_id = id;
	m_name = name;
	m_url = m_id;
}

int HkMediaChannel::open(const std::string& url, const std::string& params)
{
	if (isOpen())
	{
		close();
	}

	if (!startAudioData())
	{
		return EFAULT;
	}

	m_format.m_audioCodec = makeOutCodec(m_audioType);
	m_format.m_channels = 1;
	m_format.m_sampleBits = 16;
	m_format.m_sampleRate = 8000;

	av::AudioFormat g722Format;
	g722Format.m_codec = MEDIA_CODEC_G722;
	g722Format.m_channels = 1;
	g722Format.m_samplerate = 16000;

	av::AudioFormat g711Format;
	g711Format.m_codec = MEDIA_CODEC_G711U;
	g711Format.m_channels = 1;
	g711Format.m_samplerate = 8000;

	m_g722Encoder.open(g711Format, g722Format);
	m_g722Decoder.open(g722Format, g711Format);

	return 0;
}

void HkMediaChannel::close()
{
	stopAudioData();

	m_loginId = 0;

	m_g722Encoder.close();
	m_g722Decoder.close();

	m_audioPts = 0;
}

bool HkMediaChannel::isOpen()
{
	return (m_loginId > -1) && (m_speakHandle > -1);
}


std::string HkMediaChannel::getUrl()
{
	return m_url;
}


int HkMediaChannel::startStream()
{
	setState(STATE_PLAYING);
	return 0;
}


int HkMediaChannel::pauseStream()
{
	setState(STATE_PAUSED);
	return 0;
}


void HkMediaChannel::stopStream()
{
	setState(STATE_STOPPED);
}


int HkMediaChannel::forceKeyFrame()
{
	return 0;
}


void HkMediaChannel::onMediaFormat(const MediaFormat& fmt)
{
	// pass
}


void HkMediaChannel::onMediaPacket(MediaPacketPtr& pkt)
{
	if (!pkt)
	{
		return;
	}

	if (pkt->isVideo())
	{
		return;
	}

	if (m_speakHandle < 0)
	{
		return;
	}

	if (m_dumpEnabled)
	{
		comn::FileUtil::write(pkt->data(), pkt->size(), "dump_to_hk.pcm", true);
	}

	//CLog::debug("(%s) begin onMediaPacket. size: %d. canWrite: %d\n", m_id.c_str(), pkt->size(), m_canWrite);

	if (isG711(m_audioType))
	{
		sendVoiceData(pkt);
	}
	else
	{
		comn::AutoCritSec lock(m_csHandle);

		MediaPacketPtr outPkt(new MediaPacket());
		outPkt->set_type(MEDIA_TYPE_AUDIO);
		if (m_g722Encoder.transcode(pkt->get(), outPkt->get()))
		{
			if (m_dumpEnabled)
			{
				comn::FileUtil::write(outPkt->data(), outPkt->size(), "dump_to_hk.g722", true);
			}

			sendVoiceData(outPkt);
		}
	}
	
	//CLog::debug("(%s) end onMediaPacket. size: %d\n", m_id.c_str(), pkt->size());
}


void HkMediaChannel::onMediaEvent(int event)
{
	// pass
}

std::string HkMediaChannel::getName()
{
	return m_name;
}

std::string HkMediaChannel::getId()
{
	return m_id;
}

static void STDCALL TalkCallBack(int iDataType, void* pData, int iDataSize, void* pUser)
{
	HkMediaChannel* pthis = (HkMediaChannel*)pUser;
	pthis->onTalkCallback(iDataType, pData, iDataSize);
}

void HkMediaChannel::onTalkCallback(int iDataType, void* pData, int iDataSize)
{
	//CLog::debug("(%s) onTalkCallback. iDataType:%d, size:%d\n", m_id.c_str(), iDataType, iDataSize);

	if (m_dumpEnabled)
	{
		comn::FileUtil::write((char*)pData, iDataSize, "dump_from_hk.g722", true);
	}

	AVPacket pkt;
	av_init_packet(&pkt);
	pkt.data = (unsigned char*)pData;
	pkt.size = iDataSize;

	MediaPacketPtr outPkt(new MediaPacket());
	outPkt->set_type(MEDIA_TYPE_AUDIO);

	if (isG711(m_audioType))
	{
		outPkt->copy(pkt.data, pkt.size);
	}
	else
	{
		if (!m_g722Decoder.transcode(&pkt, outPkt->get()))
		{
			CLog::warning("(%s) failed to transcode to g711.\n", m_id.c_str());
			return;
		}
	}

	if (m_dumpEnabled)
	{
		comn::FileUtil::write(outPkt->data(), outPkt->size(), "dump_from_hk.pcm", true);
	}

	int64_t duration = outPkt->size();
	m_audioPts += duration;

	int64_t pts = m_audioPts;
	outPkt->set_dts(pts);
	outPkt->set_pts(pts);

	fireMediaPacket(outPkt);

	//CLog::debug("(%s) onTalkCallback end. iDataType:%d, size:%d, canRead: %d\n", m_id.c_str(), iDataType, iDataSize, m_canRead);
}


bool HkMediaChannel::startAudioData()
{
	char* cameraId = const_cast<char*>(m_id.c_str());

	long ret = Std_GetTalkAudioType(m_loginId, cameraId, m_audioType);
	CLog::debug("Std_GetTalkAudioType. audioType:%d, ret:%d\n", m_audioType, ret);

	av::MediaCodec audioCodec = toMediaCodec(m_audioType);
	if (audioCodec == av::MEDIA_CODEC_NONE)
	{
		CLog::warning("unknown audio type:%d\n", m_audioType);
		return false;
	}

	m_speakHandle = Std_StartTalk(m_loginId, cameraId, TalkCallBack, this);
	if (m_speakHandle < 0)
	{
		char desc[1024] = { 0 };
		Std_GetLastError(desc);
		CLog::warning("Std_StartTalk failed. error:%s\n", desc);

		stopAudioData();
		return false;
	}

	return true;
}

void HkMediaChannel::stopAudioData()
{
	if (m_speakHandle > -1)
	{
		Std_StopTalk(m_speakHandle);
		m_speakHandle = -1;
	}
}

void HkMediaChannel::enableDump(bool enabled)
{
	m_dumpEnabled = enabled;
}

av::MediaCodec HkMediaChannel::makeOutCodec(int audioType)
{
	if (audioType == STD_AUDIO_TYPE_G711U_S8K)
	{
		return av::MEDIA_CODEC_G711U;
	}
	else if (audioType == STD_AUDIO_TYPE_G711A_S8K)
	{
		return av::MEDIA_CODEC_G711A;
	}
	return av::MEDIA_CODEC_G711U;
}

av::MediaCodec HkMediaChannel::toMediaCodec(int audioType)
{
	if (audioType == STD_AUDIO_TYPE_G711U_S8K)
	{
		return av::MEDIA_CODEC_G711U;
	}
	else if (audioType == STD_AUDIO_TYPE_G711A_S8K)
	{
		return av::MEDIA_CODEC_G711A;
	}
	else if (audioType == STD_AUDIO_TYPE_G722_S16K)
	{
		return av::MEDIA_CODEC_G722;
	}
	else if (audioType == STD_AUDIO_TYPE_AAC_S32K)
	{
		return av::MEDIA_CODEC_AAC;
	}
	else if (audioType == STD_AUDIO_TYPE_G726_S8K)
	{
		return av::MEDIA_CODEC_G726;
	}
	else if (audioType == STD_AUDIO_TYPE_MPEG2_S16K)
	{
		return av::MEDIA_CODEC_MP2;
	}
	return av::MEDIA_CODEC_NONE;
}

bool HkMediaChannel::isG711(int audioType)
{
	return (audioType == STD_AUDIO_TYPE_G711U_S8K) || (audioType == STD_AUDIO_TYPE_G711A_S8K);
}

void HkMediaChannel::sendVoiceData(MediaPacketPtr& pkt)
{
	if (!pkt || pkt->size() <= 0)
	{
		return;
	}

	Std_SendVoiceData(m_speakHandle, (char*)pkt->data(), pkt->size());
}

}